//
// Copyright (c) 2008 Advanced Micro Devices, Inc. All rights reserved.
//
#include <iostream>
#include <cstdlib>

#define __NO_STD_VECTOR
#define __NO_STD_STRING

#include <cl.hpp>

inline 
void 
checkErr(cl_int err, const char * name)
{
    if (err != CL_SUCCESS) {
        std::cerr << "ERROR: " <<  name << " (" << err << ")" << std::endl;
        exit(EXIT_FAILURE);
    }
}

int
main(void)
{
    cl_int err;

    // Plaform info
    cl::vector<cl::Platform> platforms;
    err = cl::Platform::get(&platforms);

    checkErr(
        err && (platforms.size() == 0 ? -1 : CL_SUCCESS),
        "cl::Platform::get()");

    // Iteratate over platforms
    std::cout << "Number of platforms:\t\t\t\t " 
              << platforms.size() 
              << std::endl;
    for (cl::vector<cl::Platform>::iterator i = platforms.begin(); 
         i != platforms.end(); 
         ++i) {
        std::cout << "  Plaform Profile:\t\t\t\t "    
                  << (*i).getInfo<CL_PLATFORM_PROFILE>().c_str() 
                  << std::endl; 
        std::cout << "  Plaform Version:\t\t\t\t "    
                  << (*i).getInfo<CL_PLATFORM_VERSION>().c_str() 
                  << std::endl; 
        std::cout << "  Plaform Name:\t\t\t\t\t "     
                  << (*i).getInfo<CL_PLATFORM_NAME>().c_str() 
                  << std::endl; 
        std::cout << "  Plaform Vendor:\t\t\t\t "   
                  << (*i).getInfo<CL_PLATFORM_VENDOR>().c_str() << std::endl; 
        if ((*i).getInfo<CL_PLATFORM_EXTENSIONS>().size() > 0) {
            std::cout << "  Plaform Extensions:\t\t\t " 
                      << (*i).getInfo<CL_PLATFORM_EXTENSIONS>().c_str() 
                      << std::endl; 
        }
    }

    std::cout << std::endl << std:: endl;
    // Now Iteratate over each platform and its devices
    for (cl::vector<cl::Platform>::iterator p = platforms.begin(); 
         p != platforms.end(); 
         ++p) {

        std::cout << "  Plaform Name:\t\t\t\t\t "     
                  << (*p).getInfo<CL_PLATFORM_NAME>().c_str() 
                  << std::endl; 
 
        cl::vector<cl::Device> devices;
        (*p).getDevices(CL_DEVICE_TYPE_ALL, &devices);
    
        std::cout << "Number of devices:\t\t\t\t " << devices.size() << std::endl;
        for (cl::vector<cl::Device>::iterator i = devices.begin(); 
             i != devices.end(); 
             ++i) {
            
            std::cout << "  Device Type:\t\t\t\t\t " ;
            cl_device_type dtype = (*i).getInfo<CL_DEVICE_TYPE>();
            switch (dtype) {
            case CL_DEVICE_TYPE_ACCELERATOR:
                std::cout << "CL_DEVICE_TYPE_ACCRLERATOR" << std::endl;
                break;
            case CL_DEVICE_TYPE_CPU:
                std::cout << "CL_DEVICE_TYPE_CPU" << std::endl;
                break;
            case CL_DEVICE_TYPE_DEFAULT:
                std::cout << "CL_DEVICE_TYPE_DEFAULT" << std::endl;
                break;
            case CL_DEVICE_TYPE_GPU:
                std::cout << "CL_DEVICE_TYPE_GPU" << std::endl;
                break;
            }

            std::cout << "  Device ID:\t\t\t\t\t " 
                      << (*i).getInfo<CL_DEVICE_VENDOR_ID>() 
                      << std::endl;
            
            std::cout << "  Max compute units:\t\t\t\t " 
                      << (*i).getInfo<CL_DEVICE_MAX_COMPUTE_UNITS>() 
                      << std::endl;
            
            std::cout << "  Max work items dimensions:\t\t\t " 
                      << (*i).getInfo<CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS>() 
                      << std::endl;
            
            cl::vector< ::size_t> witems = 
                (*i).getInfo<CL_DEVICE_MAX_WORK_ITEM_SIZES>();
            for (int x = 0; 
                 x < (*i).getInfo<CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS>(); 
                 x++) {
                std::cout << "    Max work items[" 
                          << x << "]:\t\t\t\t " 
                          << witems[x] 
                          << std::endl;
            }

            std::cout << "  Max work group size:\t\t\t\t " 
                      << (*i).getInfo<CL_DEVICE_MAX_WORK_GROUP_SIZE>() 
                      << std::endl;
            
            std::cout << "  Preferred vector width char:\t\t\t " 
                      << (*i).getInfo<CL_DEVICE_PREFERRED_VECTOR_WIDTH_CHAR>() 
                      << std::endl;

            std::cout << "  Preferred vector width short:\t\t\t " 
                      << (*i).getInfo<CL_DEVICE_PREFERRED_VECTOR_WIDTH_SHORT>() 
                      << std::endl;
            
            std::cout << "  Preferred vector width int:\t\t\t " 
                      << (*i).getInfo<CL_DEVICE_PREFERRED_VECTOR_WIDTH_INT>() 
                      << std::endl;
            
            std::cout << "  Preferred vector width long:\t\t\t " 
                      << (*i).getInfo<CL_DEVICE_PREFERRED_VECTOR_WIDTH_LONG>() 
                      << std::endl;
            
            std::cout << "  Preferred vector width float:\t\t\t " 
                      << (*i).getInfo<CL_DEVICE_PREFERRED_VECTOR_WIDTH_FLOAT>() 
                      << std::endl;
            
            std::cout << "  Preferred vector width double:\t\t " 
                      << (*i).getInfo<CL_DEVICE_PREFERRED_VECTOR_WIDTH_DOUBLE>() 
                      << std::endl;
            
            std::cout << "  Max clock frequency:\t\t\t\t " 
                      << (*i).getInfo<CL_DEVICE_MAX_CLOCK_FREQUENCY>() 
                      << "Mhz"
                      << std::endl;
            
            std::cout << "  Address bits:\t\t\t\t\t " 
                      << (*i).getInfo<CL_DEVICE_ADDRESS_BITS>() 
                      << std::endl;        
            
            std::cout << "  Max memory allocation:\t\t\t " 
                      << (*i).getInfo<CL_DEVICE_MAX_MEM_ALLOC_SIZE>() 
                      << std::endl;        
            
            std::cout << "  Image support:\t\t\t\t " 
                      << ((*i).getInfo<CL_DEVICE_IMAGE_SUPPORT>() ? "Yes" : "No")
                      << std::endl;        
            
            if ((*i).getInfo<CL_DEVICE_IMAGE_SUPPORT>()) {
                std::cout << "  Max number of images read arguments:\t " 
                          << (*i).getInfo<CL_DEVICE_MAX_READ_IMAGE_ARGS>()
                          << std::endl;        

                std::cout << "  Max number of images write arguments:\t " 
                          << (*i).getInfo<CL_DEVICE_MAX_WRITE_IMAGE_ARGS>()
                          << std::endl;        
                
                std::cout << "  Max image 2D width:\t\t\t " 
                          << (*i).getInfo<CL_DEVICE_IMAGE2D_MAX_WIDTH>()
                          << std::endl;        

                std::cout << "  Max image 2D height:\t\t\t " 
                          << (*i).getInfo<CL_DEVICE_IMAGE2D_MAX_HEIGHT>()
                          << std::endl;        
                
                std::cout << "  Max image 3D width:\t\t\t " 
                          << (*i).getInfo<CL_DEVICE_IMAGE3D_MAX_WIDTH>()
                          << std::endl;        

                std::cout << "  Max image 3D height:\t " 
                          << (*i).getInfo<CL_DEVICE_IMAGE3D_MAX_HEIGHT>()
                          << std::endl;        
                
                std::cout << "  Max image 3D depth:\t\t\t " 
                          << (*i).getInfo<CL_DEVICE_IMAGE3D_MAX_DEPTH>()
                          << std::endl;        

                std::cout << "  Max samplers within kernel:\t\t " 
                          << (*i).getInfo<CL_DEVICE_MAX_SAMPLERS>()
                          << std::endl;        
            }

            std::cout << "  Max size of kernel argument:\t\t\t " 
                      << (*i).getInfo<CL_DEVICE_MAX_PARAMETER_SIZE>()
                      << std::endl;        
            
            std::cout << "  Alignment (bits) of base address:\t\t " 
                      << (*i).getInfo<CL_DEVICE_MEM_BASE_ADDR_ALIGN>()
                      << std::endl;        
            
            std::cout << "  Minimum alignment (bytes) for any datatype:\t " 
                      << (*i).getInfo<CL_DEVICE_MIN_DATA_TYPE_ALIGN_SIZE>()
                      << std::endl;        

            std::cout << "  Single precision floating point capability" << std::endl;
            std::cout << "    Denorms:\t\t\t\t\t " 
                      << ((*i).getInfo<CL_DEVICE_SINGLE_FP_CONFIG>() & 
                          CL_FP_DENORM ? "Yes" : "No")
                      << std::endl;
            std::cout << "    Quiet NaNs:\t\t\t\t\t " 
                      << ((*i).getInfo<CL_DEVICE_SINGLE_FP_CONFIG>() & 
                          CL_FP_INF_NAN ? "Yes" : "No")
                      << std::endl;
            std::cout << "    Round to nearest even:\t\t\t " 
                      << ((*i).getInfo<CL_DEVICE_SINGLE_FP_CONFIG>() &  
                          CL_FP_ROUND_TO_NEAREST ? "Yes" : "No")
                      << std::endl;
            std::cout << "    Round to zero:\t\t\t\t " 
                      << ((*i).getInfo<CL_DEVICE_SINGLE_FP_CONFIG>() &  
                          CL_FP_ROUND_TO_ZERO ? "Yes" : "No")
                      << std::endl;
            std::cout << "    Round to +ve and infinity:\t\t\t " 
                      << ((*i).getInfo<CL_DEVICE_SINGLE_FP_CONFIG>() &  
                          CL_FP_ROUND_TO_INF ? "Yes" : "No")
                      << std::endl;
            std::cout << "    IEEE754-2008 fused multiply-add:\t\t " 
                      << ((*i).getInfo<CL_DEVICE_SINGLE_FP_CONFIG>() &  
                          CL_FP_FMA ? "Yes" : "No")
                      << std::endl;

            std::cout << "  Cache type:\t\t\t\t\t " ;
            switch ((*i).getInfo<CL_DEVICE_GLOBAL_MEM_CACHE_TYPE>()) {
            case CL_NONE:
                std::cout << "None" << std::endl;
                break;
            case CL_READ_ONLY_CACHE:
                std::cout << "Read only" << std::endl;
                break;
            case CL_READ_WRITE_CACHE:
                std::cout << "Read/Write" << std::endl;
                break;
            }
            
            std::cout << "  Cache line size:\t\t\t\t " 
                      << (*i).getInfo<CL_DEVICE_GLOBAL_MEM_CACHELINE_SIZE>()
                      << std::endl;
            
            std::cout << "  Cache size:\t\t\t\t\t " 
                      << (*i).getInfo<CL_DEVICE_GLOBAL_MEM_CACHE_SIZE>()
                      << std::endl;
            
            std::cout << "  Global memory size:\t\t\t\t " 
                      << (*i).getInfo<CL_DEVICE_GLOBAL_MEM_SIZE>()
                      << std::endl;
            
            std::cout << "  Constant buffer size:\t\t\t\t " 
                      << (*i).getInfo<CL_DEVICE_MAX_CONSTANT_BUFFER_SIZE>()
                      << std::endl;
            
            std::cout << "  Max number of constant args:\t\t\t " 
                      << (*i).getInfo<CL_DEVICE_MAX_CONSTANT_ARGS>()
                      << std::endl;

            std::cout << "  Local memory type:\t\t\t\t " ;
            switch ((*i).getInfo<CL_DEVICE_LOCAL_MEM_TYPE>()) {
            case CL_LOCAL:
                std::cout << "Scratchpad" << std::endl;
                break;
            case CL_GLOBAL:
                std::cout << "Global" << std::endl;
                break;
            }
            

            std::cout << "  Local memory size:\t\t\t\t " 
                      << (*i).getInfo<CL_DEVICE_LOCAL_MEM_SIZE>()
                      << std::endl;
            
            std::cout << "  Profiling timer resolution:\t\t\t " 
                      << (*i).getInfo<CL_DEVICE_PROFILING_TIMER_RESOLUTION>() 
                      << std::endl;
            
            std::cout << "  Device endianess:\t\t\t\t " 
                      << ((*i).getInfo<CL_DEVICE_ENDIAN_LITTLE>() ? "Little" : "Big") 
                      << std::endl;
            
            std::cout << "  Available:\t\t\t\t\t " 
                      << ((*i).getInfo<CL_DEVICE_AVAILABLE>() ? "Yes" : "No")
                      << std::endl;
     
            std::cout << "  Compiler available:\t\t\t\t " 
                      << ((*i).getInfo<CL_DEVICE_COMPILER_AVAILABLE>() ? "Yes" : "No")
                      << std::endl;
            
            std::cout << "  Execution capabilities:\t\t\t\t " << std::endl;
            std::cout << "    Execute OpenCL kernels:\t\t\t " 
                      << ((*i).getInfo<CL_DEVICE_EXECUTION_CAPABILITIES>() & 
                          CL_EXEC_KERNEL ? "Yes" : "No")
                      << std::endl;
            std::cout << "    Execute native function:\t\t\t " 
                      << ((*i).getInfo<CL_DEVICE_EXECUTION_CAPABILITIES>() & 
                          CL_EXEC_NATIVE_KERNEL ? "Yes" : "No")
                      << std::endl;
            
            std::cout << "  Queue properties:\t\t\t\t " << std::endl;
            std::cout << "    Out-of-Order:\t\t\t\t " 
                      << ((*i).getInfo<CL_DEVICE_QUEUE_PROPERTIES>() & 
                          CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE ? "Yes" : "No")
                      << std::endl;
            std::cout << "    Profiling :\t\t\t\t\t " 
                      << ((*i).getInfo<CL_DEVICE_QUEUE_PROPERTIES>() & 
                          CL_QUEUE_PROFILING_ENABLE ? "Yes" : "No")
                      << std::endl;
            
            
            std::cout << "  Platform ID:\t\t\t\t\t " 
                  << (*i).getInfo<CL_DEVICE_PLATFORM>()
                      << std::endl;
            
            std::cout << "  Name:\t\t\t\t\t\t " 
                      << (*i).getInfo<CL_DEVICE_NAME>().c_str()
                      << std::endl;
            
            std::cout << "  Vendor:\t\t\t\t\t " 
                      << (*i).getInfo<CL_DEVICE_VENDOR>().c_str()
                      << std::endl;
            
            std::cout << "  Driver version:\t\t\t\t " 
                      << (*i).getInfo<CL_DRIVER_VERSION>().c_str()
                      << std::endl;
            
            std::cout << "  Profile:\t\t\t\t\t " 
                      << (*i).getInfo<CL_DEVICE_PROFILE>().c_str()
                      << std::endl;
            
            std::cout << "  Version:\t\t\t\t\t " 
                      << (*i).getInfo<CL_DEVICE_VERSION>().c_str()
                      << std::endl;

            std::cout << "  Extensions:\t\t\t\t\t " 
                      << (*i).getInfo<CL_DEVICE_EXTENSIONS>().c_str()
                      << std::endl;
        }
        std::cout << std::endl << std::endl;
    }

	{
		char c;
		std::cout<<"Press ENTER";
		std::cin.get(c);
	}

    return EXIT_SUCCESS;
}
